package com.sh.rbac.config;

import com.sh.rbac.filter.JwtAuthenticationFilter;
import com.sh.rbac.handler.AuthAccessDeniedHandler;
import com.sh.rbac.handler.AuthEntryPointHandler;
import com.sh.rbac.service.impl.LoginService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.List;

/**
 * security 配置类
 */

@EnableMethodSecurity  // 开启方法权限控制
@EnableWebSecurity     // 开启 Web 安全性
@AllArgsConstructor    // 构造函数注入
@Configuration         // 标记为配置类
public class SecurityConfig {

    private LoginService loginService;
    private AuthEntryPointHandler authEntryPointHandler;
    private AuthAccessDeniedHandler authAccessDeniedHandler;
    private JwtAuthenticationFilter jwtAuthenticationFilter;


    /**
     * 创建并返回一个 BCryptPasswordEncoder 实例，用于密码的加密。
     *
     * @return 返回配置好的 BCryptPasswordEncoder 实例。
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 返回一个新的 BCryptPasswordEncoder 实例，用于密码加密
        return new BCryptPasswordEncoder();
    }

    /**
     * 创建 AuthenticationManager 实例，用于处理用户认证。
     *
     * @return 返回配置好的 AuthenticationManager 实例。
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        // 设置 DaoAuthenticationProvider，用于从数据库中检索用户详情并进行密码验证
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(loginService); // 设置用户详情服务
        provider.setPasswordEncoder(this.passwordEncoder()); // 设置密码编码器
        // 创建并返回 ProviderManager，它使用配置的 AuthenticationProvider 处理认证请求
        return new ProviderManager(provider);
    }


    /**
     * 配置安全过滤链
     *
     * @param httpSecurity 用于配置HTTP安全性的对象
     * @return 返回配置好的安全过滤链
     * @throws Exception 如果配置过程中发生错误，则抛出异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // 禁用表单登录
        httpSecurity.formLogin(AbstractHttpConfigurer::disable);
        // 禁用注销
        httpSecurity.logout(AbstractHttpConfigurer::disable);
        // 禁用会话管理
        httpSecurity.sessionManagement(AbstractHttpConfigurer::disable);
        // 禁用HTTP基本身份验证
        httpSecurity.httpBasic(AbstractHttpConfigurer::disable);
        // 禁用跨站请求伪造（CSRF）保护
        httpSecurity.csrf(AbstractHttpConfigurer::disable);
        // 禁用X-Frame-Options
        httpSecurity.headers(it -> it.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));

        // 启用跨域资源共享（CORS）
        httpSecurity.cors(it -> it.configurationSource(this.corsConfigurationSource()));

        // 配置请求授权
        httpSecurity.authorizeHttpRequests(it ->
                it
                        // 静态资源允许匿名访问
                        .requestMatchers(HttpMethod.GET,"/*.html", "/*.css", "/*.js").permitAll()

                        // 登录请求允许匿名访问
                        .requestMatchers("/sys/user/login", "/sys/user/captcha", "sys/user/logout", "file/*").permitAll()

                        // 升级版 swagger 允许匿名访问
                        .requestMatchers("/doc.html", "/v3/api-docs/**", "/webjars/**").permitAll()

                        // swagger 允许匿名访问
                        //.requestMatchers("/swagger-ui/**", "/swagger-resources/**", "/swagger-ui.html").permitAll()

                        // druid 监控允许匿名访问
                        .requestMatchers("/druid/**").permitAll()

                        // spring boot admin 监控允许匿名访问
                        .requestMatchers("/admin/**", "/actuator/**").permitAll()

                        // 对其他所有请求进行身份验证
                        .anyRequest().authenticated()
        );

        // 添加 JwtAuthenticationFilter 到过滤器链中
        httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        // 配置异常处理
        httpSecurity.exceptionHandling(it ->
                // 配置认证失败处理器
                it.authenticationEntryPoint(authEntryPointHandler)
                        // 配置权限不足处理器
                        .accessDeniedHandler(authAccessDeniedHandler)
        );

        // 构建并返回安全过滤链
        return httpSecurity.build();
    }

    /**
     * 配置跨域资源共享（CORS）
     *
     * @return CorsConfigurationSource
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 允许跨域请求的域名
        configuration.setAllowedOriginPatterns(List.of("*"));
        // 允许带凭证
        configuration.setAllowCredentials(true);
        // 允许的请求方法
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "OPTIONS", "PATCH", "HEAD"));
        // 允许的请求头
        configuration.setAllowedHeaders(List.of("*"));
        // 预检请求的有效期，单位为秒
        configuration.setMaxAge(3600L);
        // 允许使用GET和POST方法
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有URL生效
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
